package com.pay.modules.alipay.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundAuthOrderUnfreezeModel;
import com.alipay.api.domain.AlipayTradePayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayFundAuthOrderUnfreezeRequest;
import com.alipay.api.request.AlipayTradePayRequest;
import com.alipay.api.response.AlipayFundAuthOrderUnfreezeResponse;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.app.models.AlipayTradeAppPayResponse;
import com.alipay.easysdk.payment.common.models.AlipayDataDataserviceBillDownloadurlQueryResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeCloseResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePayResponse;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePrecreateResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.pay.common.constants.Constants;
import com.pay.common.model.Product;
import com.pay.common.util.SslUtils;
import com.pay.common.util.ZxingUtils;
import com.pay.modules.alipay.service.CpAliPayService;
import com.pay.modules.alipay.util.CpPayUtils;
import com.pay.modules.alipay.util.CreditPayUtils;
import com.pay.payment.entity.Pay;
import com.pay.payment.service.IPayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;


/**
 * 支付宝
 * 创建者 科帮网
 * 创建时间	2018年1月15日
 * ======================
 * 商户端私钥：
 * 由我们自己生成的RSA私钥（必须与商户端公钥是一对），生成后要保存在服务端，绝对不能保存在客户端，也绝对不能从服务端下发。
 * 用来对订单信息进行加签，加签过程一定要在服务端完成，绝对不能在客户端做加签工作，客户端只负责用加签后的订单信息调起支付宝来支付。
 * ======================
 * 商户端公钥：
 * 由我们自己生成的RSA公钥（必须与商户端私钥是一对），生成后需要填写在支付宝开放平台，
 * 用来给支付宝服务端验签经过我们加签后的订单信息，以确保订单信息确实是我们商户端发给支付宝的，并且确保订单信息在传输过程中未被篡改。
 * ======================
 * 支付宝私钥：
 * 支付宝自己生成的，他们自己保存，开发者是无法看到的，用来对支付结果进行加签。
 * ======================
 * 支付宝公钥：
 * 支付宝公钥和支付宝私钥是一对，也是支付宝生成的，当我们把商户端公钥填写在支付宝开放平台后，平台就会给我们生成一个支付宝公钥。
 * 我们可以复制下来保存在服务端，同样不要保存在客户端，并且不要下发，避免被反编译或截获，而被篡改支付结果。
 * 用来让服务端对支付宝服务端返给我们的同步或异步支付结果进行验签，以确保支付结果确实是由支付宝服务端返给我们服务端的，而且没有被篡改。
 * 对支付结果的验签工作也一定要在服务端完成，绝对不能在客户端验签，因为支付宝公钥一旦存储在客户端用来验签，那就可能被反编译，这样就谁都可以验签支付结果并篡改了。
 * ======================
 * 支付宝建议加签方式升级为RSA(SHA256)密钥，以为 SHA 貌似已经被破解了。
 * 
 */
//@DubboService(group = "payCloud", retries = 1, timeout = 10000)
@Service
public class CpAliPayServiceImpl implements CpAliPayService {
	private static final Logger logger = LoggerFactory.getLogger(CpAliPayServiceImpl.class);
	
    @Value("${file.path}")
    private String filePath;
    @Value("${server.context.url}")
    private String projectUrl;
    @Autowired
    private CpPayUtils cpPayUtils;
    @Autowired
    private CreditPayUtils creditPayUtils;
    @Autowired
    private IPayService payService;

	@Override
	public String aliPay(Product product) {
		logger.info("订单号：{}生成支付宝支付码",product.getOutTradeNo());
        try {
            SslUtils.ignoreSsl();
            String orderNo = IdUtil.getSnowflake(1,1).nextId()+"";
            AlipayTradePrecreateResponse response = Factory.Payment.FaceToFace()
                    .asyncNotify(cpPayUtils.getConfig().getNotifyUrl())
                    .preCreate(product.getSubject(), orderNo, product.getTotalFee());
            if (ResponseChecker.success(response)) {
                String imgName = orderNo+".png";
                String imgPath= filePath+ Constants.SF_FILE_SEPARATOR + imgName;
                ZxingUtils.createQRCodeImage(response.getQrCode(), imgPath);
                String imgUrl = projectUrl+"/file/"+imgName;
                return imgUrl;
            } else {
                logger.error("企业支付下单失败{}{}",response.msg,response.subMsg);
                return Constants.FAIL;
            }
        } catch (Exception e) {
            logger.error("企业支付下单失败");
            return Constants.FAIL;
        }
	}

	@Override
	public String aliRefund(Product product) {
		logger.info("订单号："+product.getOutTradeNo()+"支付宝退款");
        try {
                AlipayTradeRefundResponse response = Factory.Payment
                        .Common()
                        .refund(product.getOutTradeNo(), product.getTotalFee());
                if(ResponseChecker.success(response)){
                    return Constants.SUCCESS;
                }else{
                    logger.error("订单号：{}支付宝退款失败",product.getOutTradeNo());
                    return Constants.FAIL;
                }
        } catch (Exception e) {
            logger.error("支付宝支付退款失败{}",e.getMessage());
            return Constants.FAIL;
        }
	}
    /**
     * 如果你调用的是当面付预下单接口(aliPay.trade.precreate)，调用成功后订单实际上是没有生成，因为创建一笔订单要买家、卖家、金额三要素。
     * 预下单并没有创建订单，所以根据商户订单号操作订单，比如查询或者关闭，会报错订单不存在。
     * 当用户扫码后订单才会创建，用户扫码之前二维码有效期2小时，扫码之后有效期根据timeout_express时间指定。
     * =====只有支付成功后 调用此订单才可以=====
     */
	@Override
	public String aliCloseOrder(Product product) {
		logger.info("订单号："+product.getOutTradeNo()+"支付宝关闭订单");
        try {
            AlipayTradeCloseResponse response = Factory.Payment
                    .Common()
                    .close(product.getOutTradeNo());
            if(ResponseChecker.success(response)){
                return Constants.SUCCESS;
            }else{
                logger.error("订单号：{}支付宝关闭订单失败",product.getOutTradeNo());
                return Constants.FAIL;
            }
        } catch (Exception e) {
            logger.error("支付宝支付关闭订单失败{}",e.getMessage());
            return Constants.FAIL;
        }
	}
	@Override
	public String downloadBillUrl(String billDate,String billType) {
		logger.info("获取支付宝订单地址:"+billDate);
        try {
            AlipayDataDataserviceBillDownloadurlQueryResponse response = Factory.Payment
                    .Common()
                    .downloadBill(billType,billDate);
            if(ResponseChecker.success(response)){
                return Constants.SUCCESS;
            }else{
                return Constants.FAIL;
            }
        } catch (Exception e) {
            logger.error("支付宝支付关闭订单失败{}",e.getMessage());
            return Constants.FAIL;
        }
	}

	@Override
	public String aliPayMobile(Product product) {
        try {
            AlipayTradeWapPayResponse response = Factory.Payment
                    .Wap()
                    .pay(product.getSubject(), product.getOutTradeNo(), product.getTotalFee(),"","");
            if (ResponseChecker.success(response)) {
                return response.getBody();
            } else {
                logger.error("支付宝App支付(系统)失败:{}",response.body);
                return Constants.FAIL;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return Constants.FAIL;
        }
	}
	@Override
	public String aliPayPc(Product product) {
		logger.info("支付宝PC支付下单");
        try {
            String orderNo = IdUtil.getSnowflake(1,1).nextId()+"";
            AlipayTradePagePayResponse response = Factory.Payment.Page()
                    .pay(product.getSubject(), orderNo, product.getTotalFee(),"");
            if (ResponseChecker.success(response)) {
                return Constants.FAIL;
            } else {
                return response.getBody();
            }
        } catch (Exception e) {
            logger.error("企业支付下单失败");
            return Constants.FAIL;
        }
	}
	@Override
	public String appPay(Product product) {
        try {
            AlipayTradeAppPayResponse response = Factory.Payment
                    .App()
                    .pay(product.getSubject(), product.getOutTradeNo(), product.getTotalFee());
            if (ResponseChecker.success(response)) {
                return response.getBody();
            } else {
                logger.error("支付宝App支付(系统)失败:{}",response.body);
                return Constants.FAIL;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return Constants.FAIL;
        }
	}
    @Override
    public JSONObject alipayOrderStr(String bId, Integer cstId, Integer appId, Integer productId, Integer parentId, String subject, String userName, String mobile, String source, BigDecimal totalAmount) {


        JSONObject map = new JSONObject();
        String seqNo = IdUtil.getSnowflake(1,1).nextId()+"";;
        String payType = "PRE_AUTH_ONLINE";
        String payMode = "alipay.fund.auth.order.app.freeze";
        String channelCode = "alipay";
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //使用了默认的格式创建了一个日期格式化对象。
            String timestamp = dateFormat.format(new Date());
            Map bizContent = new TreeMap();
            bizContent.put("order_title", "预授权");//商品的标题/交易标题/订单标题/订单关键字等。
            bizContent.put("out_order_no", seqNo);//商户授权资金订单号 ,不能包含除中文、英文、数字以外的字符，创建后不能修改，需要保证在商户端不重复。
            bizContent.put("out_request_no", "O"+seqNo);//商户本次资金操作的请求流水号，用于标示请求流水的唯一性，不能包含除中文、英文、数字以外的字符，需要保证在商户端不重复。
            bizContent.put("amount", totalAmount);//9.00
            bizContent.put("product_code", payType);//QUICK_MSECURITY_PAY\ PRE_AUTH_ONLINE
            logger.info("alipayOrderStr bId ={}", bId );
            if(!bId.equals("trade")){
                //业务扩展参数，用于商户的特定业务信息的传递，json格式。
                // 1.授权业务对应的类目，key为category，value由支付宝分配，比如充电桩业务传 "CHARGE_PILE_CAR"若使用信用预授权，则该值必传；
                // 2. 外部商户的门店编号，key为outStoreCode，可选；
                // 3. 外部商户的门店简称，key为outStoreAlias，可选;
                // 4. 外部商户的门店简称，key为outStoreAlias，可选;
                // 5.间联模式必须传入二级商户所属机构id，key为requestOrgId；
                // 6.信用服务Id，key为serviceId，信用场景下必传，具体值需要联系芝麻客服。
                //需要支持信用授权，该字段必传
                //model.setExtraParam("{\"category\":\"信用类目\",\"serviceId\":\"传入创建的信用服务ID\","\"outStoreCode\":\"charge001\",\"outStoreAlias\":\"充电桩北京路点\"}");
                // outStoreAlias将在用户端信用守护、支付信息、账单详情页展示
                Map extraContent = new TreeMap();
                extraContent.put("category",creditPayUtils.getConfig().getCategory());//物品租赁-数码其他
                extraContent.put("serviceId",creditPayUtils.getConfig().getServiceId());
                extraContent.put("outStoreCode",subject);
                bizContent.put("extra_param", JSON.toJSONString(extraContent));
                //商户可用该参数指定用户可使用的支付渠道，本期支持商户可支持三种支付渠道，
                // 余额宝（MONEY_FUND）、花呗（PCREDIT_PAY）以及芝麻信用（CREDITZHIMA）。
                // 商户可设置一种支付渠道，也可设置多种支付渠道。
                //[{"payChannelType":"PCREDIT_PAY"},{"payChannelType":"MONEY_FUND"}]
                bizContent.put("enable_pay_channels", "[{\"payChannelType\":\"PCREDIT_PAY\"},{\"payChannelType\":\"MONEY_FUND\"},{\"payChannelType\":\"CREDITZHIMA\"}]");
            }

            if (!payOrder(cstId, appId, productId,parentId, payType, payMode, channelCode, seqNo, userName, mobile, totalAmount,subject,source)) {
                return null;
            }
            Map orderContent = new TreeMap();
            orderContent.put("timestamp", timestamp);
            orderContent.put("method", payMode);//alipay.trade.app.pay
            orderContent.put("app_id", creditPayUtils.getConfig().getAppId());//2019061165465914 2021001163643355
            orderContent.put("charset", "utf-8");
            orderContent.put("sign_type", "RSA2");
            orderContent.put("version", "1.0");
            //bizContent.put("format","JSON");
            orderContent.put("notify_url", creditPayUtils.getConfig().getNotifyUrl()+"/callback/credit/alipay");
            orderContent.put("biz_content", JSON.toJSONString(bizContent));
            /**@param content 加签内容@param privateKey 加签私钥@param charset 加签字符集@param charset 签名方法**/
            //String AlipaySignature.rsaSign(String content, String privateKey, String charset,String signType)
            String sign = AlipaySignature.rsaSign(toString(orderContent), creditPayUtils.getConfig().getMerchantPrivateKey(), "UTF-8", "RSA2");
            orderContent.put("sign", sign);
            //orderStr = orderStr+"&sign="+sign;
            String encodedSign = toQueryString(orderContent);
            map.put("orderStr", encodedSign);
            map.put("seqOrderNo", seqNo);
        } catch (Exception e) {
            logger.info("alipayOrderStr Exception ={}", e);
        }
        return map;
    }

    /**
     * 测试预授权解冻
     * 注意事项
     1、支持全额解冻和部分解冻；
     2、解冻转支付时，若auth_confirm_mode=complete，无需调用解冻接口，支付宝端在扣款成功后会自动解冻剩余金额；若auth_confirm_mode=not_complete，在收到支付成功通知后，商户自行调用解冻接口将余额进行解冻；
     3、对于信用授权订单，扣款处理中或扣款失败（如：trade_ status=WAIT_BUYER_PAY或ORDER SUCCESS PAY INPROGRESS），若调用解冻接口，会中断支付宝扣款流程，请慎用；
     4、extraParam为选填字段，只对信用授权订单生效，若订单为信用全免订单，extraParam必须传入{"unfreezeBizInfo":"{&quot;bizComplete&quot;:&quot;true&quot;}"}，将为用户生成芝麻履约订单。
     5、信用全免订单，比如押金200元，信用全免(免押金额200元)，全额解冻的时候需要传入200元，部分解冻的时候根据实际解冻金额传入；
     通知样例 https://www.merchant.com/receive_notify.htm?notify_type=trade_status_sync&notify_id=91722adff935e8cfa58b3aabf4dead6ibe&notify_time=2017-02-16 21:46:15&sign_type=RSA2&sign=WcO+t3D8Kg71dTlKwN7r9PzUOXeaBJwp8/FOuSxcuSkXsoVYxBpsAidprySCjHCjmaglNcjoKJQLJ28/Asl93joTW39FX6i07lXhnbPknezAlwmvPdnQuI01HZsZF9V1i6ggZjBiAd5lG8bZtTxZOJ87ub2i9GuJ3Nr/NUc9VeY=&total_freeze_amount=null&total_unfreeze_amount=null&total_pay_amount=null&rest_amount=null&auth_no=null&out_order_no=null&operation_id=null&out_request_no=null&operation_type=null&amount=null&status=null&gmt_create=null&gmt_trans=null&payer_logon_id=null&payer_user_id=null&credit_amount=0.01&fund_amount=0.01&total_freeze_credit_amount=0.01&total_freeze_fund_amount=0.01&total_unfreeze_credit_amount=0.01&total_unfreeze_fund_amount=0.01&total_pay_credit_amount=0.01&total_pay_fund_amount=0.01&rest_credit_amount=0.01&rest_fund_amount=0.01
     */
    @Override
    public   Boolean   fundAuthOrderUnFreeze (String seqNo)  {
        boolean flag =false;
        try{
            QueryWrapper<Pay> queryWrapper = new QueryWrapper();
            queryWrapper.eq("seq_no", seqNo);
            queryWrapper.eq("status", 1);
            Pay pay = payService.getOne(queryWrapper);
            if(pay ==null){
                logger.warn( "alipayOrderStr 预授权解冻 payService.getOrder 没有记录！ seqNo: {}" +seqNo);
                return false;
            }
            String authNo = pay.getReferenceId();
            String outRequestNo =seqNo;
            String amount =pay.getAmount().toString();


            AlipayClient alipayClient =  new DefaultAlipayClient( "https://openapi.alipay.com/gateway.do" , creditPayUtils.getConfig().getAppId() , creditPayUtils.getConfig().getMerchantPrivateKey() , "json" , "GBK" , creditPayUtils.getConfig().getZfbPubKey() , "RSA2" );
            AlipayFundAuthOrderUnfreezeRequest request =  new  AlipayFundAuthOrderUnfreezeRequest();
            AlipayFundAuthOrderUnfreezeModel model =  new  AlipayFundAuthOrderUnfreezeModel();
            model.setAuthNo(authNo);  // 支付宝资金授权订单号，在授权冻结成功时返回需要入库保存
            model.setOutRequestNo(outRequestNo); //同一商户每次不同的资金操作请求，商户请求流水号不能重复,且与冻结流水号不同
            model.setAmount(amount);  // 本次操作解冻的金额，单位为：元（人民币），精确到小数点后两位
            model.setRemark( "预授权解冻" );  // 商户对本次解冻操作的附言描述，长度不超过100个字母或50个汉字
            //选填字段，信用授权订单，针对信用全免订单，传入该值完结信用订单，形成芝麻履约记录
            model.setExtraParam( "{\"unfreezeBizInfo\":\"{\\\"bizComplete\\\":\\\"true\\\"}\"}" );
            request.setBizModel(model);
            request.setNotifyUrl( creditPayUtils.getConfig().getNotifyUrl()+"/callback/credit/fundAuthOrderUnFreeze"); //异步通知地址，必填，该接口只通过该参数进行异步通知

            AlipayFundAuthOrderUnfreezeResponse response = alipayClient.execute(request);
            logger.info( " 预授权解冻 response: {}" +response.getBody());
            if(response.getStatus().equals("SUCCESS")){
                pay.setStatus(4);
                boolean i = payService.updateById(pay);
                if(i){
                    flag =true;
                    logger.info( "alipayOrderStr 预授权解冻 payService.getOrder 状态更改成 授权解冻中！ seqNo: {}" +seqNo);
                }
            }
        } catch (Exception e) {
            logger.info("alipayOrderStr Exception ={}", e);
        }
        return flag;
    }


    /**
     * 测试
     * 入参TIPS:
     1、product_code=PRE_AUTH_ONLINE，产品码，是固定值，不要更改；
     2、subject（业务订单描述），请准确描述实际消费订单说明。如发生和实际消费场景不匹配的说明，造成不必要的客诉，会影响到正常的服务使用；
     3、需要传入store_id（建议与资金授权冻结接口中extra_param参数的outStoreCode取值一致）参数。如果需返佣接入时还需要传入"extend_params":{"sys_service_provider_id":"ISV签约账户的PID"}参数用于计算返佣；
     4、auth_confirm_mode传入COMPLETE，无需调用解冻接口，支付宝端在扣款成功后会自动解冻剩余金额，同时该笔授权订单完成；若auth_confirm_mode=NOT_COMPLETE，在收到支付成功通知后，商户自行调用解冻接口将余额进行解冻；
     5、如果需要从一笔授权中完成多笔订单支付，保持auth_no不变，不同订单根据outTradeNo进行标识，此时auth_confirm_mode不传或者传入NOT_COMPLETE；进行到最后一笔转支付时，auth_confirm_mode传入COMPLETE由支付宝完成剩余金额自动解冻，或者商户自行调用解冻接口将剩余金额解冻；
     出参处理：
     1、response中的code=10000，表示接口调用成功，支付状态以异步通知为准；
     2、auth_trade_pay_mode，预授权支付模式，该参数仅在信用预授权支付场景下返回。信用预授权支付：CREDIT_PREAUTH_PAY；
     3、如果未收到异步通知，可以通过交易查询接口查询最新支付状态，如果返回处理中（如：trade_ status=WAIT_BUYER_PAY或ORDER SUCCESS PAY INPROGRESS）场景，商户可以调用预授权转支付（首次扣款15天内都可重试），重新发起扣款（注意：商户订单号out_trade_no保持不变）；
     风险提示： 信用预授权订单转支付，可能会出现一定概率的资金或货物损失，请充分知晓相关权责，详见产品服务协议。
     通知样例 https://www.merchant.com/receive_notify.htm?notify_type=trade_status_sync&notify_id=91722adff935e8cfa58b3aabf4dead6ibe&notify_time=2017-02-16 21:46:15&sign_type=RSA2&sign=WcO+t3D8Kg71dTlKwN7r9PzUOXeaBJwp8/FOuSxcuSkXsoVYxBpsAidprySCjHCjmaglNcjoKJQLJ28/Asl93joTW39FX6i07lXhnbPknezAlwmvPdnQuI01HZsZF9V1i6ggZjBiAd5lG8bZtTxZOJ87ub2i9GuJ3Nr/NUc9VeY=&app_id=2088102146225135&trade_no=2013112011001004330000121536&out_trade_no=6823789339978248&out_biz_no=85478536823789339978248&buyer_logon_id=159****5620&seller_id=2088102146225135&trans_currency=USD&seller_email=aa@mail.sa.com&total_amount=120.88&buyer_id=2088202954065786&settle_currency=USD&settle_amount=88.88&pay_currency=CNY&pay_amount=580.04&settle_trans_rate=1&trans_pay_rate=6.5261&trade_status=TRADE_SUCCESS&invoice_amount=88.88&receipt_amount=88.88&point_amount=88.88&refund_fee=10.23&buyer_pay_amount=88.88&subject=Iphone6 16G&body=Iphone6 16G&gmt_create=2014-11-27 15:45:57&gmt_payment=2014-11-27 15:45:57&gmt_refund=2014-11-27 14:15:57&gmt_close=2014-11-27 17:45:57&fund_bill_list="[{\"amount\":\"10000.00\",\"fundChannel\":\"ALIPAYACCOUNT\"}]"&voucher_detail_list="[{\"id\":\"20170307000730026487005X1M6V\",\"name\":\"全仓5折优惠券\"}]"&discount_goods_detail="[{\"goodsId\":\"STANDARD1026181538\",\"goodsName\":\"雪碧\",\"discountAmount\":\"10.00\"}]"&refund_preset_paytool_list=[{"amount":"1.00","assert_type_code":"HEMA"}]&notify_action_type=payByAccountAction/closeTradeAction/reverseAction/finishFPAction&discount_amount=88.88&mdiscount_amount=88.88&unfreeze_amount=18.18&auth_trade_pay_mode=CREDIT_PREAUTH_PAY
     */
    @Override
    public   Boolean   tradePay (String seqNo)  {
        boolean flag =false;
        try{
            QueryWrapper<Pay> queryWrapper = new QueryWrapper();
            queryWrapper.eq("seq_no", seqNo);
            queryWrapper.eq("status", 1);
            Pay pay = payService.getOne(queryWrapper);
            if(pay ==null){
                logger.warn( "alipayOrderStr 授权转支付 payService.getOrder 没有记录！ seqNo: {}" +seqNo);
                return false;
            }
            String authNo=pay.getReferenceId();
            String outRequestNo=IdUtil.getSnowflake(1,1).nextId()+"";;
            String amount =pay.getAmount().toString();
            String subject=pay.getSubject();
            String payUserId=pay.getPayerVa();
            AlipayClient alipayClient =  new  DefaultAlipayClient( "https://openapi.alipay.com/gateway.do" , creditPayUtils.getConfig().getAppId() , creditPayUtils.getConfig().getMerchantPrivateKey() , "json" , "utf-8" , creditPayUtils.getConfig().getZfbPubKey() , "RSA2" );

            AlipayTradePayRequest request =  new AlipayTradePayRequest();

            AlipayTradePayModel model =  new  AlipayTradePayModel();
            model.setOutTradeNo( outRequestNo );  // 预授权转支付商户订单号，为新的商户交易流水号；如果重试发起扣款，商户订单号不要变；
            model.setProductCode( "PRE_AUTH_ONLINE" );  // 固定值PRE_AUTH_ONLINE
            model.setAuthNo( authNo);  // 填写预授权冻结交易号
            model.setSubject( "预授权转支付" );  // 解冻转支付标题，用于展示在支付宝账单中
            model.setTotalAmount( amount);  // 结算支付金额
            model.setSellerId(creditPayUtils.getConfig().getSellerId());  // 填写卖家支付宝账户pid //如果该值为空，则默认为商户签约账号对应的支付宝用户ID
            model.setBuyerId(payUserId);  // 填写预授权用户uid，通过预授权冻结接口返回的payer_user_id字段获取
            model.setStoreId( subject); // 填写实际交易发生的终端编号，与预授权的outStoreCode保持一致即可
            model.setBody( "预授权解冻转支付" );  // 可填写备注信息
            model.setAuthConfirmMode( "COMPLETE" ); //必须使用COMPLETE,传入该值用户剩余金额会自动解冻
            request.setBizModel(model);
            request.setNotifyUrl(creditPayUtils.getConfig().getNotifyUrl()+"/callback/credit/tradePay"); //异步通知地址，必填，该接口只通过该参数进行异步通知

            com.alipay.api.response.AlipayTradePayResponse response = alipayClient.execute(request);
            if (response.isSuccess()){
                System.out.println( "调用成功" );
                logger.info( "alipayOrderStr 授权转支付 调用成功 response: {}" +response.getBody());
                pay.setStatus(2);
                boolean i = payService.updateById(pay);
                if(i){
                    flag=true;
                    logger.info( "alipayOrderStr 授权转支付 payService.getOrder 状态更改成 支付中！ seqNo: {}" +seqNo);
                }
            }  else  {
                logger.info( "alipayOrderStr 授权转支付 调用失败 response: {}" +response.getBody());

            }
        } catch (Exception e) {
            logger.info("alipayOrderStr Exception ={}", e);
        }
        return flag;
    }




    public Boolean payOrder(Integer cstId, Integer appId, Integer productId,Integer parentId, String payType, String payMode, String channelCode
            ,  String seqNo, String userName, String mobile, BigDecimal amount,String subject,String source) {
        Pay pay = new Pay();
        pay.setCstId(cstId);
        pay.setAppId(appId);
        pay.setParentId(parentId);
        pay.setProductId(productId);
        pay.setPayType(payType);
        pay.setPayMode(payMode);
        pay.setChannelCode(channelCode);
        pay.setSeqNo(seqNo);
        pay.setUserName(userName);
        pay.setMobile(mobile);
        pay.setAmount(amount);
        pay.setApplyRepayTime(LocalDateTime.now());
        pay.setSource(source.replaceAll(" ",""));
        pay.setSubject(subject);
        return payService.save(pay);
    }




    private static String toQueryString(Map<String, String> data) throws UnsupportedEncodingException {
        StringBuffer queryString = new StringBuffer();
        for (Map.Entry<String, String> pair : data.entrySet()) {
            if (pair.getValue() == null || pair.getValue().equals("")) {
                continue;
            }
            queryString.append(pair.getKey() + "=");
            queryString.append(URLEncoder.encode(pair.getValue(), "UTF-8") + "&");
        }
        if (queryString.length() > 0) {
            queryString.deleteCharAt(queryString.length() - 1);
        }
        return queryString.toString();
    }

    private static String toString(Map<String, String> data) {
        StringBuffer queryString = new StringBuffer();
        for (Map.Entry<String, String> pair : data.entrySet()) {
            if (pair.getValue() == null || pair.getValue().equals("")) {
                continue;
            }
            queryString.append(pair.getKey() + "=");
            queryString.append(pair.getValue() + "&");
        }
        if (queryString.length() > 0) {
            queryString.deleteCharAt(queryString.length() - 1);
        }
        return queryString.toString();
    }




}
